﻿using System;
using System.Collections.Generic;
using System.Text;
using ttbit.form.impl;
using ttbit.math;

namespace ttbit.core
{
    public enum BlendMode
    {
        None,
        Blend,
        Add,
    }
    public class CommonRenderParam
    {
        //一些全局的参数

        public float[] matWorld;
        public float[] matView;
        public float[] matProj;

        public CommonRenderParam()
        {
            matWorld = Mat3.Create();
            matView = Mat3.Create();
            matProj = Mat3.Create();

        }
    }

    public class Material : IDisposable
    {

        int sampler = -1;
        public BlendMode BlendMode = BlendMode.Blend;
        public Material(ShaderPool.ShaderProgram prog)
        {
            ChangeShader(prog);
        }
        Texture? _texMain;
        public Texture? texMain
        {
            get
            {
                return _texMain;
            }
            set
            {
                if (_uni_texmain!=null)
                {
                    _texMain = value;
                    _uni_texmain.texid = _texMain == null ? 0 : _texMain.TexID;
                }
            }
        }
        ColorF _color;
        //材质Color属于变色，轻易不要用
        public ColorF color
        {
            get
            {
                return _color;
            }
            set
            {
                if (_uni_color != null)
                {
                    _color = value;
                    _uni_color.value[0] = _color.R;
                    _uni_color.value[1] = _color.G;
                    _uni_color.value[2] = _color.B;
                    _uni_color.value[3] = _color.A;
                }
            }
        }
        UniformValue _uni_texmain = null;
        UniformValue _uni_color = null;
        UniformValue _uni_matWorld = null;
        UniformValue _uni_matView = null;
        UniformValue _uni_matProj = null;

        public void ChangeShader(ShaderPool.ShaderProgram prog)
        {
            this.prog = prog;
            if (uniforms == null)
            {
                uniforms = new List<UniformValue>();
            }
            else
            {
                uniforms.Clear();
            }
            if (nameduniforms == null)
            {
                nameduniforms = new Dictionary<string, UniformValue>();
            }
            else
            {
                nameduniforms.Clear();
            }
            foreach (var u in prog.uniformpos)
            {
                var utype = prog.GetUniformType(u.Key);
                var uv = new UniformValue(utype, u.Value);
                uniforms.Add(uv);
                nameduniforms[u.Key] = uv;
                if (u.Key == "texmain")
                    _uni_texmain = uv;
                if (u.Key == "color")
                {
                    _uni_color = uv;
                    color = ColorF.White;
                }
                if (u.Key == "matWorld")
                    _uni_matWorld = uv;

                if (u.Key == "matView")
                    _uni_matView = uv;

                if (u.Key == "matProj")
                    _uni_matProj = uv;
            }
            if (sampler < 0)
            {

                sampler = Graphics.GL.CreateSampler();

                Graphics.GL.SamplerParameter(sampler, IGL.SamplerParameterName.TextureWrapS, IGL.SamplerValue.Clamp);
                Graphics.GL.SamplerParameter(sampler, IGL.SamplerParameterName.TextureWrapT, IGL.SamplerValue.Clamp);
                Graphics.GL.SamplerParameter(sampler, IGL.SamplerParameterName.TextureMagFilter, IGL.SamplerValue.Nearest);
                Graphics.GL.SamplerParameter(sampler, IGL.SamplerParameterName.TextureMinFilter, IGL.SamplerValue.Nearest);
            }

        }
        public ShaderPool.ShaderProgram prog
        {
            get;
            private set;
        }
        List<UniformValue> uniforms;
        Dictionary<string, UniformValue> nameduniforms;
        public UniformValue GetUniform(string name)
        {
            if (nameduniforms.TryGetValue(name, out UniformValue v))
            {
                return v;
            }
            return null;
        }
        public void Apply()
        {
            if (BlendMode== BlendMode.None)
            {
                Graphics.GL.Disable(IGL.EnableCap.Blend);
            }
            else if(BlendMode== BlendMode.Blend){
                Graphics.GL.Enable(IGL.EnableCap.Blend);
                Graphics.GL.BlendEquation(IGL.BlendEquationMode.FuncAdd);
                // Color =srcrgb* SrcAlpha + destrgb*OneMinusSrcAlpha;
                Graphics.GL.BlendFuncSeparate(IGL.BlendingFactorSrc.SrcAlpha, IGL.BlendingFactorDest.OneMinusSrcAlpha, IGL.BlendingFactorSrc.SrcAlpha, IGL.BlendingFactorDest.One);
            }
            else if (BlendMode == BlendMode.Add)
            {
                Graphics.GL.Enable(IGL.EnableCap.Blend);
                Graphics.GL.BlendEquation(IGL.BlendEquationMode.FuncAdd);
                // Color =srcrgb* SrcAlpha + destrgb*OneMinusSrcAlpha;
                Graphics.GL.BlendFuncSeparate(IGL.BlendingFactorSrc.SrcAlpha, IGL.BlendingFactorDest.One, IGL.BlendingFactorSrc.SrcAlpha, IGL.BlendingFactorDest.One);
            }


            Graphics.GL.Disable(IGL.EnableCap.CullFace);//关闭背面裁剪

            Graphics.GL.UseProgram(prog.progid);
            int texindex = 0;
            foreach (var u in uniforms)
            {

                switch (u.unitype)
                {
                    case ShaderPool.UniformType.BOOL:
                    case ShaderPool.UniformType.INT:
                        Graphics.GL.Uniform1i(u.uniid, u.intvalue[0]);
                        break;
                    case ShaderPool.UniformType.FLOAT:
                        Graphics.GL.Uniform1f(u.uniid, u.value[0]);
                        break;
                    case ShaderPool.UniformType.VEC2:
                        Graphics.GL.Uniform2f(u.uniid, u.value[0], u.value[1]);
                        break;
                    case ShaderPool.UniformType.VEC3:
                        Graphics.GL.Uniform3f(u.uniid, u.value[0], u.value[1], u.value[2]);
                        break;
                    case ShaderPool.UniformType.VEC4:
                        Graphics.GL.Uniform4f(u.uniid, u.value[0], u.value[1], u.value[2], u.value[3]);
                        break;
                    case ShaderPool.UniformType.MAT2:
                        Graphics.GL.UniformMatrix2(u.uniid, 1, false, u.value);
                        break;
                    case ShaderPool.UniformType.MAT3:
                        Graphics.GL.UniformMatrix3(u.uniid, 1, false, u.value);
                        break;
                    case ShaderPool.UniformType.MAT2X3:
                        Graphics.GL.UniformMatrix2x3(u.uniid, 1, false, u.value);
                        break;
                    case ShaderPool.UniformType.MAT4:
                        Graphics.GL.UniformMatrix4(u.uniid, 1, false, u.value);
                        break;
                    case ShaderPool.UniformType.SAMPLER2D:
                        {
                            Graphics.GL.ActiveTexture(IGL.TextureUnit.Texture0 + texindex);
                            Graphics.GL.BindTexture2D(u.texid);
                            Graphics.GL.BindSampler(texindex, sampler);
                            Graphics.GL.Uniform1i(u.uniid, texindex);//0=texture 0;
                            texindex++;
                            break;
                        }
                    default:
                        throw new NotImplementedException();
                }
            }
        }

        public void UpdateContext(CommonRenderParam context)
        {
            //nameduniforms["matWorld"].value = context.matWorld;
            //nameduniforms["matView"].value = context.matView;
            //nameduniforms["matProj"].value = context.matProj;
            _uni_matWorld.value = context.matWorld;
            _uni_matView.value = context.matView;
            _uni_matProj.value = context.matProj;
        }
        public void Dispose()
        {
            if (sampler >= 0)
            {
                Graphics.GL.DeleteSampler(sampler);
            }
        }
    }

}